import { HvigorNode, HvigorPlugin } from '@ohos/hvigor';
import { OhosAppContext, OhosHapContext, OhosPluginId, Target } from '@ohos/hvigor-ohos-plugin';
import { ClassDeclaration, Decorator, Project, SourceFile, CodeBlockWriter, Type, Node, ObjectLiteralExpression, ArrowFunction, Scope, ts, ArrayLiteralExpression, PropertyAssignment } from "ts-morph";
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { version } from "../package.json";

export interface ClassTransformerCodeGenConfig {
  enable?: boolean, // 是否开启该插件的实际功能, 默认仅 release 包开启
  preventModifySourceCode?: boolean, // 是否避免修改源码, 默认 true

  whiteDirList?: string[],
  blackDirList?: string[]
}

// hvigor 对象不从外面传入的话, nodesEvaluated 不会生效, why?
export function ClassTransformerCodeGenPlugin(hvigor: any, config: ClassTransformerCodeGenConfig = {}): HvigorPlugin {
  return {
    pluginId: 'ClassTransformerCodeGenPlugin',
    apply(currentNode: HvigorNode) {
      console.log('[ClassTransformerCodeGenPlugin] apply');
      hvigor.nodesEvaluated(() => {
        console.log('[ClassTransformerCodeGenPlugin] nodesEvaluated');
        if (!('enable' in config)) {
          const appContext = currentNode.getContext(OhosPluginId.OHOS_APP_PLUGIN) as OhosAppContext;
          config.enable = appContext.getBuildMode().startsWith('release');
        }
        if (!('preventModifySourceCode' in config)) {
          config.preventModifySourceCode = true;
        }
        console.log(`[ClassTransformerCodeGenPlugin] enable: ${config.enable}, preventModifySourceCode: ${config.preventModifySourceCode}`);
        if (!config.enable) {
          return;
        }
        currentNode.subNodes((node: HvigorNode) => {
          console.log(`[ClassTransformerCodeGenPlugin] node: ${node.getNodePath()}`);
          const hapContext = node.getContext(OhosPluginId.OHOS_HAP_PLUGIN) as OhosHapContext
          hapContext?.targets((target: Target) => {
            const targetName = target.getTargetName();
            const rootPath = currentNode.getNodePath();
            const processedConfigPath = `${rootPath}/build/${targetName}/cache/${targetName}/${targetName}@GenTransformerCode/processed.config`;
            console.log(`[ClassTransformerCodeGenPlugin] processedConfigPath: ${processedConfigPath}`);
            const backupSuffix = '.backup';
            
            console.log(`[ClassTransformerCodeGenPlugin] registerTask: ${targetName}@GenTransformerCode`);

            const terminateOnException = async (block: () => Promise<void>) => {
              try {
                await block();
              } catch(err) {
                setTimeout(() => {
                  throw err;
                }, 0);
              }
            };

            node.registerTask({
              name: `${targetName}@GenTransformerCode`,
              async run() {
                console.log(`[GenTransformerCode] run`);

                let processedConfig: ProcessedConfig | undefined;
                try {
                  const processedConfigStr = await fs.readFile(processedConfigPath, 'utf8');
                  // console.log(`[GenTransformerCode] run, processedConfigStr: ${processedConfigStr}`);
                  processedConfig = JSON.parse(processedConfigStr);
                } catch(err) {
                  console.error(`[GenTransformerCode] run, parse processedConfig fail`, (err as Error)?.message, (err as Error)?.stack);
                }

                const srcPaths: string[] = [];
                if (config?.whiteDirList) {
                  srcPaths.push(...config.whiteDirList.map(path => `${rootPath}${path}/**/*{.ets,.ts}`));
                }
                if (config?.blackDirList) {
                  srcPaths.push(...config.blackDirList.map(path => `!${rootPath}${path}/**/*{.ets,.ts}`));
                }
                if (0 === srcPaths.length) {
                  srcPaths.push(`${rootPath}/**/*{.ets,.ts}`);
                }
                await terminateOnException(() => 
                  ClassTransformerCodeGenProcessor.processAllSource(srcPaths, processedConfigPath, processedConfig, backupSuffix, config.preventModifySourceCode!!));
              },
              postDependencies: [`${targetName}@CompileArkTS`]
            });

            console.log(`[ClassTransformerCodeGenPlugin] registerTask: ${targetName}@CleanTransformerCode`);
            node.registerTask({
              name: `${targetName}@CleanTransformerCode`,
              async run() {
                console.log(`[CleanTransformerCode] run`);

                let processedConfig;
                try {
                  const processedConfigStr = await fs.readFile(processedConfigPath, 'utf8');
                  // console.log(`[CleanTransformerCode] run, processedConfigStr: ${processedConfigStr}`);
                  processedConfig = JSON.parse(processedConfigStr);
                } catch(err) {
                  console.error(`[CleanTransformerCode] run, parse processedConfig fail`, (err as Error)?.message, (err as Error)?.stack);
                }
                
                if (processedConfig) {
                  await terminateOnException(() => 
                    ClassTransformerCodeCleanProcessor.process(config.preventModifySourceCode!!, processedConfig, backupSuffix));
                }
              },
              dependencies: [`${targetName}@CompileArkTS`],
              postDependencies: [`${targetName}@PackageHap`]
            });
          });
        });
      });
    }
  }
}

interface ProcessedConfig {
  version?: string,
  preventModifySourceCode?: boolean
}

interface PropInfo {
  name: string,
  type: Type,
  exposeDecorator?: Decorator,
  typeDecorator?: Decorator,
  transformDecorator?: Decorator,
  excludeDecorator?: Decorator
}

interface DiscriminatorInfo {
  name: string,
  discriminatorLiteral: string,
  keep: boolean
}

class ClassTransformerCodeCleanProcessor {
  public static async process(preventModifySourceCode: boolean, processedConfig: ProcessedConfig, backupSuffix: string) {
    if (preventModifySourceCode) {
      for (const srcPath of Object.keys(processedConfig)) {
        if (srcPath === 'version' || srcPath == 'preventModifySourceCode') {
          continue;
        }
        try {
          await fs.unlink(srcPath);
          await fs.rename(srcPath + backupSuffix, srcPath);
        } catch(err) {
          console.error(`[ClassTransformerCodeCleanProcessor] run, clean ${srcPath} fail`, (err as Error)?.message, (err as Error)?.stack);
        }
      }
      return;
    }
    for (const srcPath of Object.keys(processedConfig)) {
      if (srcPath === 'version' || srcPath == 'preventModifySourceCode') {
        continue;
      }
      try {
        await fs.unlink(srcPath + backupSuffix);
      } catch(err) {
        console.error(`[ClassTransformerCodeCleanProcessor] run, clean ${srcPath} fail`, (err as Error)?.message, (err as Error)?.stack);
      }
    }
  }
}

class ClassTransformerCodeGenProcessor {
  private static readonly TAG = 'ClassTransformerCodeGenProcessor';

  private static readonly STR_REGEX = /('|")(.*)('|")/;
  private static readonly VALUE_REGEX = /:\s*(.*)/;

  // srcPaths: .js 文件处理会有问题
  // project.addSourceFilesAtPaths("folder/**/*{.d.ts,.ts}");
  // project.addSourceFilesAtPaths(["folder/file.ts", "folder/otherFile.ts"]);
  // project.addSourceFilesAtPaths(["**/*.ts", "!**/*.d.ts"]);
  public static async processAllSource(
    srcPaths: string[], 
    processedConfigPath: string, processedConfig: ProcessedConfig | undefined, 
    backupSuffix: string, preventModifySourceCode: boolean
  )  {
    this.logD(`processAllSource[${srcPaths.join(', ')}]`);
    
    // 900 工具链上主站 addSourceFilesAtPaths 非常慢(小时级别), 原因不明, 手动遍历文件夹然后用 addSourceFileAtPath 添加试试
    const project = new Project();
    for (const srcPath of srcPaths) {
      const dirPath = srcPath.substring(0, srcPath.length - 15); // '/**/*{.ets,.ts}'.length = 15 
      for (const filePath of await this.listFilesRecursive(dirPath)) {
        await fs.copyFile(filePath, filePath + backupSuffix);
        // this.logD(`${new Date().toISOString()}: >>> processAllSource, addSourceFileAtPath ${filePath}`);
        project.addSourceFileAtPath(filePath);
        // this.logD(`${new Date().toISOString()}: <<< processAllSource, addSourceFileAtPath ${filePath}`);
      }
    }
    // this.logD(`${new Date().toISOString()}: >>> processAllSource, getSourceFiles`);
    const srcFiles = project.getSourceFiles();
    // this.logD(`${new Date().toISOString()}: <<< processAllSource, getSourceFiles`);
    // const srcFiles = new Project().addSourceFilesAtPaths(srcPaths);
    
    const tasks = [];
    if (processedConfig && (version != processedConfig.version || processedConfig.preventModifySourceCode)) {
      processedConfig = undefined;
    }
    for (const srcFile of srcFiles) {
      tasks.push(this.processSource(srcFile, processedConfig));
      // await this.processSource(srcFile, processedConfig); // 串行实现
    }
    await Promise.all(tasks);

    await fs.mkdir(path.dirname(processedConfigPath), { recursive: true });
    processedConfig = { version, preventModifySourceCode };
    for (const srcFile of srcFiles) {
      // catch 住，避免一个文件的信息写失败导致 ClassTransformerCodeCleanProcessor 所有文件都不自动删除
      try {
        const srcPath: string = srcFile.getFilePath();
        // this.logD(`processAllSource, srcPath: ${srcPath}`);
        const lastModified = (await fs.stat(srcPath)).mtime.getTime();
        Reflect.set(processedConfig, srcPath, lastModified);
      } catch(err) {
        this.logE(`processAllSource, stat fail`, err as Error);
      }
    }
    // catch 住，这里失败可能只是不能自动删除，其他流程都 ok，对于 CI 来说是可以接受的
    try {
      await fs.writeFile(processedConfigPath, JSON.stringify(processedConfig));
    } catch(err) {
      this.logE(`processAllSource, write processed config fail`, err as Error);
    }
  }

  private static async listFilesRecursive(dirPath: string): Promise<string[]> {
    const files: string[] = [];
    for (const name of await fs.readdir(dirPath)) {
      const path = `${dirPath}/${name}`;
      if ((await fs.stat(path)).isDirectory()) {
        files.push(...await this.listFilesRecursive(path));
        continue;
      }
      files.push(path);
    }
    return files;
  }
  
  private static async processSource(srcFile: SourceFile, processedConfig: ProcessedConfig | undefined) {
    // 单个文件处理失败，让整个流程终止
    const srcPath: string = srcFile.getFilePath();

    if (processedConfig) {
      const oldLastModified = Reflect.get(processedConfig, srcPath);
      if (oldLastModified) {
        // catch 住，只影响增量生成
        try {
          const lastModified = (await fs.stat(srcPath)).mtime.getTime();
          // this.logD(`processSource[${srcPath}], oldLastModified: ${oldLastModified}, lastModified: ${lastModified}`);
          if (oldLastModified == lastModified) {
            return;
          }
        } catch(err) {
          this.logE(`processSource[${srcPath}], stat fail`, err as Error);
        }
      }
    }

    // .ets 后缀的文件处理会有问题, why? 先 rename 成 .ts, 处理完再恢复
    const isEts = '.ets' === srcFile.getExtension();
    let file = srcFile;
    let path: any = srcPath;
    if (isEts) {
      path = srcPath.substring(0, /* .ets */srcPath.length - 4) + '.ts';
      file = srcFile.move(path);
    }
    this.logD(`processSource[${srcPath}], path: ${path}`);

    // 遍历类处理, 添加函数
    let anyClsProcessed = false;
    for (const cls of file.getClasses()) {
      // 单个类处理失败，让整个流程终止
      const processed = await this.processClass(cls);
      anyClsProcessed =  anyClsProcessed || processed; 
    }
    this.logD(`processSource[${srcPath}], processed: ${anyClsProcessed}`);
    if (!anyClsProcessed) {
      if (isEts) {
        file = file.move(srcPath);
      }
      return;
    }

    // 添加 import
    file.getImportDeclaration((importDeclaration) => {
      const statement = importDeclaration.getText();
      // this.logD(`processSource[${srcPath}], statement: ${statement}`);
      return statement.includes('@kwai_harmony/quick-transformer');
    })?.remove();
    file.addImportDeclaration({
      namedImports: ['SupportQuickTransform', '__TransformationType', '__ClassTransformOptions', 'QuickTransformer', 'QuickType'],
      moduleSpecifier: '@kwai_harmony/quick-transformer'
    });

    if (isEts) {
      file = file.move(srcPath);
    }
    await file.save();
  }

  // 对于单个类，分析类的继承关系，成员变量名，成员变量装饰器（主要是装饰器传参需要使用）
  private static async processClass(cls: ClassDeclaration): Promise<boolean> {
    const clsName = cls.getName();
    this.logD(`processClass[${clsName}]`);

    // step 1. 先判断类是否被 class-transformer 相关的装饰器装饰, 如果不是, 不做处理
    let exposeAll = false;
    for (const decorator of cls.getDecorators()) {
      if (decorator.isDecoratorFactory() && 'Expose' === decorator.getName()) {
        exposeAll = true;
        break;
      }
    }
    const allPropInfo: PropInfo[] = [];
    for (const prop of cls.getInstanceProperties()) {
      let exposeDecorator, typeDecorator, transformDecorator, excludeDecorator;
      for (const decorator of prop.getDecorators()) {
        // isDecoratorFactory = true: @Type()
        // isDecoratorFactory = false: @Type
        if (!decorator.isDecoratorFactory()) {
          continue;
        }
        const decoratorName = decorator.getName();
        if ('Expose' === decoratorName) {
          exposeDecorator = decorator;
        } else if ('Type' === decoratorName) {
          typeDecorator = decorator;
        } else if ('Transform' === decoratorName) {
          transformDecorator = decorator;
        } else if ('Exclude' === decoratorName) {
          excludeDecorator = decorator;
        }
      }
      // 1. exposeAll 的情况，所有属性全部处理（exclude 属性后面处理
      // 2. 非 exposeAll 的情况，exposeDecorator / type / transform 都做处理
      if (exposeAll || (exposeDecorator || typeDecorator || transformDecorator)) {
        const name = prop.getName();
        this.logD(`processClass[${clsName}], find prop: ${name}`);
        const type = prop.getType();
        // this.logD(`processClass[${clsName}][${name}] type: ${type}`);
        allPropInfo.push({
          name,
          type,
          exposeDecorator,
          typeDecorator,
          transformDecorator,
          excludeDecorator
        });
      }
    }
    if (0 === allPropInfo.length) {
      this.logD(`processClass[${clsName}], skip it`);
      return false;
    }

    // step2. 信息收集: 只需要收集基类, 成员变量及其装饰器 step1 已经 on the fly 完成收集
    // 主站中 getBaseClass 拿不到, why?
    const parentClsName = cls.getExtends()?.getText();
    this.logD(`processClass[${clsName}], parentClsName: ${parentClsName}`);

    // step3. 构建 fromPlain 函数体
    const fromPlainWriter = (writer: CodeBlockWriter) => {
      writer.writeLine(`// >>>>> THIS IS AN AUTOGENERATED FUNCTION. DO NOT EDIT THIS FUNCTION DIRECTLY. <<<<<`);
      writer.writeLine('');

      if (parentClsName) {
        writer.writeLine(`if (!QuickTransformer.supportQuickTransform(${parentClsName})) QuickTransformer.assert('${clsName} 的基类 ${parentClsName} 不支持 quick transform!');`);
        writer.writeLine(`if ((${parentClsName} as ESObject)['__fromPlain__']) (${parentClsName} as ESObject)['__fromPlain__'](plain, that, options);`);
        writer.writeLine('');
      }

      for (const propInfo of allPropInfo) {
        const propName = propInfo.name;

        if (exposeAll && propInfo.excludeDecorator) {
          const excludeOptions = propInfo.excludeDecorator.getArguments()?.at(0);
          // @Exclude() 的属性不处理
          if (!excludeOptions) {
            this.logD(`processClass[${clsName}][${propName}], exclude`);
            continue;
          }
          // @Exclude({ toClassOnly: true })
          // NOTE: 只处理对象字面量的情况
          if (Node.isObjectLiteralExpression(excludeOptions)) {
            // getText: `toClassOnly: true`
            const toClassOnlyStr = (excludeOptions as ObjectLiteralExpression).getProperty('toClassOnly')?.getText();
            if (toClassOnlyStr) {
              const match = toClassOnlyStr.match(this.VALUE_REGEX);
              const toClassOnly = match && Boolean(match[1]);
              if (toClassOnly) {
                this.logD(`processClass[${clsName}][${propName}], exclude toClassOnly = true`);
                continue;
              }
            }
          } else {
            throw Error(`类 ${clsName} 的属性 ${propName} 上的装饰器 @Exclude 的使用方式不支持！！！`);
          }
        }

        const needCheckExclude = !propInfo.exposeDecorator;
        if (needCheckExclude) {
          writer.writeLine(`if (!(options?.excludeExtraneousValues ?? false)) {`);
        }
        const lineStartSpace = needCheckExclude ? '  ' : '';

        let keyName = propName;
        // @Expose({ name: 'mtype' })
        // export declare function Expose(options?: ExposeOptions): PropertyDecorator & ClassDecorator;
        const exposeOptions = propInfo.exposeDecorator?.getArguments()?.at(0);
        // NOTE: 只处理对象字面量的情况
        if (Node.isObjectLiteralExpression(exposeOptions)) {
          // getText: `name: 'mtype'`
          const nameStr = (exposeOptions as ObjectLiteralExpression).getProperty('name')?.getText();
          if (nameStr) {
            const match = nameStr.match(this.STR_REGEX);
            keyName = match ? match[2] : keyName;
            this.logD(`processClass[${clsName}][${propName}], nameStr: ${nameStr}, keyName: ${keyName}`);
          }
        } else if (exposeOptions) {
          throw Error(`类 ${clsName} 的属性 ${propName} 上的装饰器 @Expose 的使用方式不支持！！！`);
        }

        // NOTE: 优先处理 Transform 装饰器: Type & Transform 装饰器同时存在的情况, 以 Transform 装饰器为准
        // @Transform((params: TransformFnParams) => transformMeta(CommonMeta, params))
        // export declare function Transform(transformFn: (params: TransformFnParams) => any, options?: TransformOptions): PropertyDecorator;
        const transformFunc = propInfo.transformDecorator?.getArguments()?.at(0)?.getText();
        this.logD(`processClass[${clsName}][${propName}], transformFunc: ${transformFunc}`);

        // @Type(() => BaseFeed)
        // export declare function Type(typeFunction?: (type?: TypeHelpOptions) => Function, options?: TypeOptions): PropertyDecorator;
        let typeName;
        const typeFunc = propInfo.typeDecorator?.getArguments()?.at(0);
        // NOTE: 只处理箭头函数, 且函数体中直接返回类型的情况
        if (Node.isArrowFunction(typeFunc)) {
          typeName = (typeFunc as ArrowFunction).getBodyText();
          this.logD(`processClass[${clsName}][${propName}], typeName: ${typeName}`);
        } else if (typeFunc) {
          throw Error(`类 ${clsName} 的属性 ${propName} 上的装饰器 @Type 的使用方式不支持！！！`);
        }
        const discriminatorInfo = this.processTypeOptions(propName, propInfo.typeDecorator?.getArguments()?.at(1));

        const isArray = (propInfo.type.isArray() || propInfo.type.isTuple()) ?? false;
        if (transformFunc) {
          if (isArray) {
            writer.writeLine(`${lineStartSpace}if (Array.isArray(plain['${keyName}'])) {`);
            writer.writeLine(`${lineStartSpace}  const length = plain['${keyName}'].length;`);
            writer.writeLine(`${lineStartSpace}  const __${propName}: ESObject[] = new Array(length);`);
            writer.writeLine(`${lineStartSpace}  for (let i = 0; i < length; i++) {`);
            writer.writeLine(`${lineStartSpace}    __${propName}[i] = (${transformFunc})({value: plain['${keyName}'][i], key: '${keyName}', obj: plain, type: __TransformationType.PLAIN_TO_CLASS, options: QuickTransformer.enhanceOptions(options)});`);
            writer.writeLine(`${lineStartSpace}  }`);
            writer.writeLine(`${lineStartSpace}  that.${propName} = __${propName};`);
            writer.writeLine(`${lineStartSpace}}`);
          } else {
            writer.writeLine(`${lineStartSpace}const __${propName}: ESObject = (${transformFunc})({value: plain['${keyName}'], key: '${keyName}', obj: plain, type: __TransformationType.PLAIN_TO_CLASS, options: QuickTransformer.enhanceOptions(options)});`);
            writer.writeLine(`${lineStartSpace}if (__${propName} !== undefined) that.${propName} = __${propName};`);
          }
        } else if (typeName) {
          if (isArray) {
            if (discriminatorInfo) {
              writer.writeLine(`${lineStartSpace}${discriminatorInfo.discriminatorLiteral}`);
            }
            writer.writeLine(`${lineStartSpace}if (Array.isArray(plain['${keyName}'])) {`);
            writer.writeLine(`${lineStartSpace}  const length = plain['${keyName}'].length;`);
            writer.writeLine(`${lineStartSpace}  const __${propName}: ESObject[] = new Array(length);`);
            writer.writeLine(`${lineStartSpace}  for (let i = 0; i < length; i++) {`);
            if (discriminatorInfo) {
              writer.writeLine(`${lineStartSpace}    const ${discriminatorInfo.name}: ESObject = plain['${keyName}'][i] ? plain['${keyName}'][i]['${discriminatorInfo.name}'] : undefined;`);

              const typeResolveExpression = `${discriminatorInfo.name} ? (__${propName}Discriminator[${discriminatorInfo.name}] ?? ${typeName}) : ${typeName}`;
              let deleteLine = '';
              // ets 不允许使用 delete
              // if (!discriminatorInfo.keep) {
              //   deleteLine = `delete __value['${discriminatorInfo.name}'];`;
              // }
              writer.writeLine(`${lineStartSpace}    const __typeName: ESObject = ${typeResolveExpression}; ${deleteLine}`);
            } else {
              writer.writeLine(`${lineStartSpace}    const __typeName: ESObject = ${typeName};`);
            }
            writer.writeLine(`${lineStartSpace}    __${propName}[i] = QuickTransformer.plainToInstance(__typeName, plain['${keyName}'][i], options);`);
            writer.writeLine(`${lineStartSpace}  }`);
            writer.writeLine(`${lineStartSpace}  that.${propName} = __${propName};`);
            writer.writeLine(`${lineStartSpace}}`);
          } else {
            if (discriminatorInfo) {
              writer.writeLine(`${lineStartSpace}${discriminatorInfo.discriminatorLiteral}`);

              writer.writeLine(`${lineStartSpace}const ${discriminatorInfo.name}: ESObject = plain['${keyName}'] ? plain['${keyName}']['${discriminatorInfo.name}'] : undefined;`);

              const typeResolveExpression = `${discriminatorInfo.name} ? (__${propName}Discriminator[${discriminatorInfo.name}] ?? ${typeName}) : ${typeName}`;
              let deleteLine = '';
              // ets 不允许使用 delete
              // if (!discriminatorInfo.keep) {
              //   deleteLine = `delete plain['${keyName}']['${discriminatorInfo.name}'];`;
              // }
              writer.writeLine(`${lineStartSpace}const __${propName}Type: ESObject = ${typeResolveExpression}; ${deleteLine}`);
            } else {
              writer.writeLine(`${lineStartSpace}const __${propName}Type: ESObject = ${typeName};`);
            }
            writer.writeLine(`${lineStartSpace}const __${propName}: ESObject = QuickTransformer.plainToInstance(__${propName}Type, plain['${keyName}'], options);`);
            writer.writeLine(`${lineStartSpace}if (__${propName} !== undefined) that.${propName} = __${propName};`);
          }
        } else {
          if (isArray) {
            writer.writeLine(`${lineStartSpace}const __${propName}: ESObject = QuickTransformer.toArrayIfNeed(plain['${keyName}']);`);
          } else {
            writer.writeLine(`${lineStartSpace}const __${propName}: ESObject = QuickTransformer.toObjectIfNeed(plain['${keyName}']);`);
          }
          writer.writeLine(`${lineStartSpace}if (__${propName} !== undefined) that.${propName} = __${propName};`);
        }

        if (needCheckExclude) {
          writer.writeLine(`}`);
        }
        writer.writeLine('');
      }
    }

    // step4. 给类添加 fromPlain 静态函数
    cls.getStaticMethod('__fromPlain__')?.remove();
    cls.addMethod({
      scope: Scope.Public,
      isStatic: true,
      name: '__fromPlain__',
      parameters: [
        { name: 'plain', type: 'ESObject' },
        { name: 'that', type: clsName },
        { name: 'options', type: '__ClassTransformOptions', hasQuestionToken: true }
      ],
      returnType: 'void',
      statements: fromPlainWriter
    });

    return true;
  }

  private static processTypeOptions(propName: string, typeOptions: Node<ts.Node> | undefined): DiscriminatorInfo | undefined {
    // NOTE: 只处理对象字面量的情况
    if (!Node.isObjectLiteralExpression(typeOptions)) {
      return;
    }

    const discriminatorProp = (typeOptions as ObjectLiteralExpression).getProperty('discriminator');
    if (!Node.isPropertyAssignment(discriminatorProp)) {
      this.logE(`processTypeOptions[${propName}], discriminatorProp not PropertyAssignment`);
      throw Error(`${propName} 上 @Type 装饰器 discriminator 参数的使用方式不支持！！！`);
    }
    const discriminator = (discriminatorProp as PropertyAssignment).getInitializer();
    if (!Node.isObjectLiteralExpression(discriminator)) {
      this.logE(`processTypeOptions[${propName}], discriminator not ObjectLiteralExpression`);
      throw Error(`${propName} 上 @Type 装饰器 discriminator 参数的使用方式不支持！！！`);
    }
    const propertyStr = (discriminator as ObjectLiteralExpression).getProperty('property')?.getText();
    let match = propertyStr?.match(this.VALUE_REGEX);
    if (!match) {
      this.logE(`processTypeOptions[${propName}], property not match, propertyStr: ${propertyStr}`);
      throw Error(`${propName} 上 @Type 装饰器 discriminator 参数的使用方式不支持！！！`);
    }

    const nameStr = match[1];
    const nameMatch = nameStr.match(this.STR_REGEX);
    if (!nameMatch) {
      this.logE(`processTypeOptions[${propName}], name not match, nameStr: ${nameStr}`);
      throw Error(`${propName} 上 @Type 装饰器 discriminator 参数的使用方式不支持！！！`);
    }
    const name = nameMatch[2];

    const subTypesProp = (discriminator as ObjectLiteralExpression).getProperty('subTypes');
    if (!Node.isPropertyAssignment(subTypesProp)) {
      this.logE(`processTypeOptions[${propName}], subTypesProp not PropertyAssignment`);
      throw Error(`${propName} 上 @Type 装饰器 discriminator 参数的使用方式不支持！！！`);
    }
    const subTypes = (subTypesProp as PropertyAssignment).getInitializer();
    if (!Node.isArrayLiteralExpression(subTypes)) {
      this.logE(`processTypeOptions[${propName}], subTypes not ArrayLiteralExpression`);
      throw Error(`${propName} 上 @Type 装饰器 discriminator 参数的使用方式不支持！！！`);
    }

    let discriminatorLiteral = `const __${propName}Discriminator: ESObject = {};`;
    for (const element of (subTypes as ArrayLiteralExpression).getElements()) {
      // 任一元素不是对象字母量的情况, 不做处理
      if (!Node.isObjectLiteralExpression(element)) {
        this.logE(`processTypeOptions[${propName}], element not ObjectLiteralExpression, element: ${element.getText()}`);
        throw Error(`${propName} 上 @Type 装饰器 discriminator 参数的使用方式不支持！！！`);
      }

      const nameStr = (element as ObjectLiteralExpression).getProperty('name')?.getText();
      match = nameStr?.match(this.VALUE_REGEX);
      if (!match) {
        this.logE(`processTypeOptions[${propName}], name not match, nameStr: ${nameStr}`);
        throw Error(`${propName} 上 @Type 装饰器 discriminator 参数的使用方式不支持！！！`);
      }
      const name = match[1];

      const valueStr = (element as ObjectLiteralExpression).getProperty('value')?.getText();
      match = valueStr?.match(this.VALUE_REGEX);
      if (!match) {
        this.logE(`processTypeOptions[${propName}], value not match, valueStr: ${valueStr}`);
        throw Error(`${propName} 上 @Type 装饰器 discriminator 参数的使用方式不支持！！！`);
      }
      const value = match[1];
      discriminatorLiteral += `__${propName}Discriminator[${name}] = ${value};`;
    }

    let keep = false;
    const keepDiscriminatorPropertyStr = (typeOptions as ObjectLiteralExpression).getProperty('keepDiscriminatorProperty')?.getText();
    if (keepDiscriminatorPropertyStr) {
      match = keepDiscriminatorPropertyStr?.match(this.STR_REGEX);
      keep = match ? !!match[2] : keep;
    } 

    this.logD(`processTypeOptions[${propName}], name: ${name}, discriminatorLiteral: ${discriminatorLiteral}, keep: ${keep}`);
    return { name, discriminatorLiteral, keep };
  }

  private static logD(msg: string) {
    console.debug(`[${this.TAG}] ${msg}`);
  }

  private static logI(msg: string) {
    console.info(`[${this.TAG}] ${msg}`);
  }

  private static logE(msg: string, error?: Error) {
    console.error(`[${this.TAG}] ${msg}`, error?.name, error?.message, error?.stack);
  }
}
